package net.apexes.wsonrpc.demo.client;

import net.apexes.wsonrpc.client.Wsonrpc;
import net.apexes.wsonrpc.client.WsonrpcClient;
import net.apexes.wsonrpc.client.WsonrpcClientMessageListener;
import net.apexes.wsonrpc.client.WsonrpcClientStatusListener;
import net.apexes.wsonrpc.core.GZIPBinaryWrapper;
import net.apexes.wsonrpc.demo.api.DemoService;
import net.apexes.wsonrpc.demo.api.PushService;
import net.apexes.wsonrpc.demo.api.RegisterService;
import net.apexes.wsonrpc.demo.api.model.Dept;
import net.apexes.wsonrpc.demo.api.model.Role;
import net.apexes.wsonrpc.demo.api.model.User;
import net.apexes.wsonrpc.demo.client.service.PushServiceImpl;
import net.apexes.wsonrpc.demo.util.SimpleWsonrpcLogger;
import net.apexes.wsonrpc.json.support.gson.GsonImplementor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author hedyn
 */
public class WsonrpcClientWarpper {

    private static final Logger LOG = LoggerFactory.getLogger(WsonrpcClientWarpper.class);

    private static final int TIMEOUT = 7000;

    private final WsonrpcClient client;
    private final ExecutorService executor;

    WsonrpcClientWarpper(String clientId) {
        String url = "ws://localhost:8080/wsonrpc/" + clientId;
        executor = Executors.newFixedThreadPool(4);
        client = Wsonrpc.config()
                .executor(executor)
                .json(GsonImplementor.DEFAULT)
                .wrapper(GZIPBinaryWrapper.DEFAULT)
//                .connector(new net.apexes.wsonrpc.client.support.JavaWebsocketConnector())
//                .connector(new net.apexes.wsonrpc.client.support.TyrusWebsocketConnector())
                .logger(SimpleWsonrpcLogger.DEFAULT)
                .heartbeatSeconds(30)
                .reconnectSeconds(5, 10, 1)
//                .pingProvider(() -> String.valueOf(System.currentTimeMillis()).getBytes())
                .client(url);
        client.getServiceRegistry().allSimpleName()
                .bind(PushService.class).toInstance(new PushServiceImpl(this));
        client.setReadyCallback(() -> {
            LOG.info("...");
            wsonrpcService(RegisterService.class).register(clientId);
        });
        client.setStatusListener(new WsonrpcClientStatusListener() {
            @Override
            public void onConnected(net.apexes.wsonrpc.client.WsonrpcClient client) {
                LOG.info("onConnected...");
            }

            @Override
            public void onDisconnected(net.apexes.wsonrpc.client.WsonrpcClient client, int code, String reason) {
                LOG.info("onDisconnected...");
            }

            @Override
            public void onClose(net.apexes.wsonrpc.client.WsonrpcClient client) {
                LOG.info("onClose...");
            }
        });
        client.setMessageListener(new WsonrpcClientMessageListener() {

            @Override
            public void onSentMessage(net.apexes.wsonrpc.client.WsonrpcClient client, byte[] bytes) {
            }

            @Override
            public void onSentPing(net.apexes.wsonrpc.client.WsonrpcClient client, byte[] bytes) {
            }

            @Override
            public void onReceivePong(net.apexes.wsonrpc.client.WsonrpcClient client, byte[] bytes) {
                LOG.info("onRecvPong : " + new String(bytes));
            }
        });
    }

    boolean isConnected() {
        return client.isConnected();
    }

    void connect() throws Exception {
        if (!client.isConnected()) {
            client.connect();
        }
    }

    void disconnect() {
        if (client.isConnected()) {
            client.disconnect();
        }
    }

    void close() {
        client.close();
        executor.shutdownNow();
    }

    public <T> T wsonrpcService(Class<T> serviceClass) {
        return Wsonrpc.invoker(client)
                .simpleName()
                .timeout(TIMEOUT)
                .trace(() -> String.valueOf(System.currentTimeMillis()))
                .get(serviceClass);
    }

    void login(String username, String password) {
        if (client.isConnected()) {
            RegisterService service = wsonrpcService(RegisterService.class);
            User user = service.login(username, password);
            LOG.info("{}", user);
        }
    }

    void ping() throws Exception {
        if (client.isConnected()) {
            String text = "PingByClient:" + System.currentTimeMillis();
            client.ping(text.getBytes());
        }
    }

    void demo() {
        DemoService service = wsonrpcService(DemoService.class);
//        LOG.info("{}", service.echo("Hello wsonrpc!"));
//        LOG.info("{}", service.login("admin", "admin"));
        LOG.info("{}", service.getRoleList());
        LOG.info("{}", service.getDept("1"));
        LOG.info("{}", service.getDeptList());
        LOG.info("{}", service.listUser(Arrays.asList("admin", "1001")));

        Random random = new Random();

        Role role = new Role();
        role.setId(random.nextInt());
        role.setName(String.valueOf(role.getId()));
        service.addRole(role);

        User user = new User();
        user.setUsername(String.valueOf(System.currentTimeMillis()));
        user.setName("Name:" + user.getUsername());
        user.setAge(random.nextInt(100));
        LOG.info("{}", service.addUser(user));

        Dept dept = new Dept();
        dept.setName(String.valueOf(random.nextInt()));
        dept.setRoleList(Collections.singletonList(role));
        dept.setUserList(Collections.singletonList(user));
        LOG.info("{}", service.addDept(dept));

        LOG.info("echo2 : user={}", service.echo2(user));
        LOG.info("echo2 : role={}", service.echo2(role));
        LOG.info("echo2 : dept={}", service.echo2(dept));
    }

    void error() {
        wsonrpcService(PushService.class).setupStatus("ddd");
    }

}
